/**
 * Date instance methods
 *
 * @author Ken Snyder (ken d snyder at gmail dot com)
 * @date 2008-09-10
 * @version 2.0.2 (http://kendsnyder.com/sandbox/date/)
 * @license Creative Commons Attribution License 3.0 (http://creativecommons.org/licenses/by/3.0/)
 */
(function () {
    var day = 24 * 60 * 60 * 1000; var zeroPad = function (number, digits) { number = String(number); while (number.length < digits) number = '0' + number; return number; }; var multipliers = {
        millisecond: 1, second: 1000, minute: 60 * 1000, hour: 60 * 60 * 1000, day: day, week: 7 * day, month: {
            add: function (d, number) {
                multipliers.year.add(d, Math[number > 0 ? 'floor' : 'ceil'](number / 12)); var prevMonth = d.getMonth() + (number % 12); if (prevMonth == 12) { prevMonth = 0; d.setYear(d.getFullYear() + 1); } else if (prevMonth == -1) { prevMonth = 11; d.setYear(d.getFullYear() - 1); }
                d.setMonth(prevMonth);
            }, diff: function (d1, d2) { var diffYears = d1.getFullYear() - d2.getFullYear(); var diffMonths = d1.getMonth() - d2.getMonth() + (diffYears * 12); var diffDays = d1.getDate() - d2.getDate(); return diffMonths + (diffDays / 30); }
        }, year: { add: function (d, number) { d.setYear(d.getFullYear() + Math[number > 0 ? 'floor' : 'ceil'](number)); }, diff: function (d1, d2) { return multipliers.month.diff(d1, d2) / 12; } }
    }; for (var unit in multipliers) { if (unit.substring(unit.length - 1) != 's') { multipliers[unit + 's'] = multipliers[unit]; } }
    var format = function (d, code) { if (Date.prototype.strftime.formatShortcuts[code]) { return d.strftime(Date.prototype.strftime.formatShortcuts[code]); } else { var getter = (Date.prototype.strftime.formatCodes[code] || '').split('.'); var nbr = d['get' + getter[0]] ? d['get' + getter[0]]() : ''; if (getter[1]) nbr = zeroPad(nbr, getter[1]); return nbr; } }; var instanceMethods = {
        succ: function (unit) { return this.clone().add(1, unit); }, add: function (number, unit) {
            var factor = multipliers[unit] || multipliers.day; if (typeof factor == 'number') { this.setTime(this.getTime() + (factor * number)); } else { factor.add(this, number); }
            return this;
        }, diff: function (dateObj, unit, allowDecimal) {
            dateObj = Date.create(dateObj); if (dateObj === null) return null; var factor = multipliers[unit] || multipliers.day; if (typeof factor == 'number') { var unitDiff = (this.getTime() - dateObj.getTime()) / factor; } else { var unitDiff = factor.diff(this, dateObj); }
            return (allowDecimal ? unitDiff : Math[unitDiff > 0 ? 'floor' : 'ceil'](unitDiff));
        }, strftime: function (formatStr) {
            var source = formatStr || '%Y-%m-%d', result = '', match; while (source.length > 0) { if (match = source.match(Date.prototype.strftime.formatCodes.matcher)) { result += source.slice(0, match.index); result += (match[1] || '') + format(this, match[2]); source = source.slice(match.index + match[0].length); } else { result += source, source = ''; } }
            return result;
        }, getShortYear: function () { return this.getYear() % 100; }, getMonthNumber: function () { return this.getMonth() + 1; }, getMonthName: function () { return Date.MONTHNAMES[this.getMonth()]; }, getAbbrMonthName: function () { return Date.ABBR_MONTHNAMES[this.getMonth()]; }, getDayName: function () { return Date.DAYNAMES[this.getDay()]; }, getAbbrDayName: function () { return Date.ABBR_DAYNAMES[this.getDay()]; }, getDayOrdinal: function () { return Date.ORDINALNAMES[this.getDate() % 10]; }, getHours12: function () { var hours = this.getHours(); return hours > 12 ? hours - 12 : (hours == 0 ? 12 : hours); }, getAmPm: function () { return this.getHours() >= 12 ? 'PM' : 'AM'; }, getUnix: function () { return Math.round(this.getTime() / 1000, 0); }, getGmtOffset: function () { var hours = this.getTimezoneOffset() / 60; var prefix = hours < 0 ? '+' : '-'; hours = Math.abs(hours); return prefix + zeroPad(Math.floor(hours), 2) + ':' + zeroPad((hours % 1) * 60, 2); }, getTimezoneName: function () { var match = /(?:\((.+)\)$| ([A-Z]{3}) )/.exec(this.toString()); return match[1] || match[2] || 'GMT' + this.getGmtOffset(); }, toYmdInt: function () { return (this.getFullYear() * 10000) + (this.getMonthNumber() * 100) + this.getDate(); }, clone: function () { return new Date(this.getTime()); }
    }; for (var name in instanceMethods) Date.prototype[name] = instanceMethods[name]; var staticMethods = {
        create: function (date) {
            if (date instanceof Date) return date; if (typeof date == 'number') return new Date(date * 1000); var parsable = String(date).replace(/^\s*(.+)\s*$/, '$1'), i = 0, length = Date.create.patterns.length, pattern; var current = parsable; while (i < length) {
                ms = Date.parse(current); if (!isNaN(ms)) return new Date(ms); pattern = Date.create.patterns[i]; if (typeof pattern == 'function') { obj = pattern(current); if (obj instanceof Date) return obj; } else { current = parsable.replace(pattern[0], pattern[1]); }
                i++;
            }
            return NaN;
        }, MONTHNAMES: 'January February March April May June July August September October November December'.split(' '), ABBR_MONTHNAMES: 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '), DAYNAMES: 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'.split(' '), ABBR_DAYNAMES: 'Sun Mon Tue Wed Thu Fri Sat'.split(' '), ORDINALNAMES: 'th st nd rd th th th th th th'.split(' '), ISO: '%Y-%m-%dT%H:%M:%S.%N%G', SQL: '%Y-%m-%d %H:%M:%S', daysInMonth: function (year, month) {
            if (month == 2)
                return new Date(year, 1, 29).getDate() == 29 ? 29 : 28; return [undefined, 31, undefined, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
        }
    }; for (var name in staticMethods) Date[name] = staticMethods[name]; Date.prototype.strftime.formatCodes = { matcher: /()%(#?(%|[a-z]))/i, Y: 'FullYear', y: 'ShortYear.2', m: 'MonthNumber.2', '#m': 'MonthNumber', B: 'MonthName', b: 'AbbrMonthName', d: 'Date.2', '#d': 'Date', e: 'Date', A: 'DayName', a: 'AbbrDayName', w: 'Day', o: 'DayOrdinal', H: 'Hours.2', '#H': 'Hours', I: 'Hours12.2', '#I': 'Hours12', p: 'AmPm', M: 'Minutes.2', '#M': 'Minutes', S: 'Seconds.2', '#S': 'Seconds', s: 'Unix', N: 'Milliseconds.3', '#N': 'Milliseconds', O: 'TimezoneOffset', Z: 'TimezoneName', G: 'GmtOffset' }; Date.prototype.strftime.formatShortcuts = { F: '%Y-%m-%d', T: '%H:%M:%S', X: '%H:%M:%S', x: '%m/%d/%y', D: '%m/%d/%y', '#c': '%a %b %e %H:%M:%S %Y', v: '%e-%b-%Y', R: '%H:%M', r: '%I:%M:%S %p', t: '\t', n: '\n', '%': '%' }; Date.create.patterns = [[/-/g, '/'], [/st|nd|rd|th/g, ''], [/(3[01]|[0-2]\d)\s*\.\s*(1[0-2]|0\d)\s*\.\s*([1-9]\d{3})/, '$2/$1/$3'], [/([1-9]\d{3})\s*-\s*(1[0-2]|0\d)\s*-\s*(3[01]|[0-2]\d)/, '$2/$3/$1'], function (str) {
        var match = str.match(/^(?:(.+)\s+)?([1-9]|1[012])(?:\s*\:\s*(\d\d))?(?:\s*\:\s*(\d\d))?\s*(am|pm)\s*$/i); if (match) {
            if (match[1]) { var d = Date.create(match[1]); if (isNaN(d)) return; } else { var d = new Date(); d.setMilliseconds(0); }
            var hour = parseFloat(match[2]); hour = match[5].toLowerCase() == 'am' ? (hour == 12 ? 0 : hour) : (hour == 12 ? 12 : hour + 12); d.setHours(hour, parseFloat(match[3] || 0), parseFloat(match[4] || 0)); return d;
        }
    }];
})(); var $D = Date.create;